/**
 * Copyright 2008 Mathias Doenitz, http://lis.to/
 *
 * This file is part of the lis.to java desktop client. The lis.to java desktop client is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * The lis.to java desktop client is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with the lis.to java desktop client.
 * If not, see http://www.gnu.org/licenses/
 */

package listo.utils.swing;

import listo.utils.MiscUtils;
import org.apache.commons.lang.StringUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class HyperlinkLabel extends JLabel implements MouseListener, MouseMotionListener {

    public static final String LINK_CLICKED = "HyperlinkLabel.LinkClicked";
    public static final Color DEFAULT_LINK_COLOR = Color.BLUE;

    private Rectangle[] linkBounds;
    private MiscUtils.UrlMatch[] urlMatches;
    private Color linkColor = DEFAULT_LINK_COLOR;
    private boolean strikeThrough;
    private boolean forceUnderline;
    private boolean isPaintingLink = false;

    private int overLink = -1;

    public HyperlinkLabel() {
        addMouseListener(this);
        addMouseMotionListener(this);
        setUI(HyperlinkLabelUI.instance);
    }

    public HyperlinkLabel(String text) {
        this(text, null, LEADING);
    }

    public HyperlinkLabel(Icon image) {
        this(null, image, CENTER);
    }

    public HyperlinkLabel(String text, int horizontalAlignment) {
        this(text, null, horizontalAlignment);
    }

    public HyperlinkLabel(Icon image, int horizontalAlignment) {
        this(null, image, horizontalAlignment);
    }

    public HyperlinkLabel(String text, Icon icon, int horizontalAlignment) {
        this();
        setText(text);
        setIcon(icon);
        setHorizontalAlignment(horizontalAlignment);
        setAlignmentX(LEFT_ALIGNMENT);
    }

    public Color getLinkColor() {
        return linkColor;
    }

    public void setLinkColor(Color linkColor) {
        this.linkColor = linkColor;
    }

    public boolean isStrikeThrough() {
        return strikeThrough;
    }

    public void setStrikeThrough(boolean strikeThrough) {
        this.strikeThrough = strikeThrough;
    }

    public boolean isForceUnderline() {
        return forceUnderline;
    }

    public void setForceUnderline(boolean forceUnderline) {
        this.forceUnderline = forceUnderline;
    }

    protected boolean isPaintingLink() {
        return isPaintingLink;
    }

    protected void setPaintingLink(boolean isPaintingLink) {
        this.isPaintingLink = isPaintingLink;
    }

    public int getOverLink() {
        return overLink;
    }

    public MiscUtils.UrlMatch[] getUrlMatches() {
        return urlMatches;
    }

    public void setLinkBounds(Rectangle[] linkBounds) {
        this.linkBounds = linkBounds;
    }

    public void setText(String text) {
        // prevent the underlying JLabel from interpreting html by prepending an invisible tab
        if (StringUtils.isNotEmpty(text) && text.toLowerCase().startsWith("<html>")) text = '\t' + text;
        urlMatches = MiscUtils.findUrls(text);
        overLink = -1;
        linkBounds = null;
        super.setText(text);
    }

    public int isOverLink(Point point) {
        if (linkBounds != null) {
            for (int i = 0; i < linkBounds.length; i++) {
                if (linkBounds[i].contains(point)) return i;
            }
        }
        return -1;
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseClicked(MouseEvent e) {
        int overLink = isOverLink(e.getPoint());
        if (isEnabled() && e.getButton() == MouseEvent.BUTTON1 && overLink != -1) {
            fireActionPerformed(new ActionEvent(this, overLink, LINK_CLICKED));
        }
    }

    public void mouseEntered(MouseEvent e) {
        updateCursor(e);
    }

    public void mouseExited(MouseEvent e) {
        this.overLink = -1;
        repaint();
        setCursor(Cursor.getDefaultCursor());
    }

    public void mouseMoved(MouseEvent e) {
        updateCursor(e);
    }

    public void mouseDragged(MouseEvent e) {
        updateCursor(e);
    }

    private void updateCursor(MouseEvent e) {
        int overLink = isOverLink(e.getPoint());
        if (isEnabled() && this.overLink == -1 && overLink != -1) {
            this.overLink = overLink;
            repaint();
            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        } else if (overLink == -1 && this.overLink != -1) {
            this.overLink = -1;
            repaint();
            setCursor(Cursor.getDefaultCursor());
        }
    }

    public void addActionListener(ActionListener al) {
        listenerList.add(ActionListener.class, al);
    }

    public void removeActionListener(ActionListener al) {
        listenerList.remove(ActionListener.class, al);
    }

    public void fireActionPerformed(ActionEvent aev) {
        for (Object al : listenerList.getListeners(ActionListener.class)) {
            ((ActionListener) al).actionPerformed(aev);
        }
    }

    public Color getForeground() {
        return isPaintingLink() ? linkColor : super.getForeground();
    }

}
